package cn.yangliu.nacos.oauth2.service.impl;

import java.time.LocalDateTime;
import java.util.Objects;
import java.util.Optional;

import javax.annotation.PostConstruct;

import cn.yangliu.jpa.base.service.impl.JpaServiceImpl;
import cn.yangliu.jpa.handler.JpaHandler;
import cn.yangliu.nacos.comm.enums.GlobalResultCode;
import cn.yangliu.nacos.comm.ex.GlobalException;
import cn.yangliu.nacos.comm.model.Oauth2UserModel;
import cn.yangliu.nacos.comm.model.Oauth2UserQueryModel;
import cn.yangliu.nacos.oauth2.entity.User;
import cn.yangliu.nacos.oauth2.model.UserQueryModel;
import cn.yangliu.nacos.oauth2.repository.UserRepository;
import cn.yangliu.nacos.oauth2.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * The interface User service.
 *
 * @author 问道于盲
 * @date 2019 -03-20 00:08
 */
@Service
@Slf4j
@Transactional(rollbackFor = Throwable.class)
public class UserServiceImpl
        extends JpaServiceImpl<User, Long, UserRepository> implements UserService {

    /**
     * current class instance's member.
     * The Jpa handler.
     */
    private JpaHandler jpaHandler = JpaHandler.INSTANCE;

    /**
     * current class instance's member.
     * The Password encoder.
     */
    @Autowired
    private PasswordEncoder passwordEncoder;

    /**
     * Init.
     */
    @PostConstruct
    public void init() {
        try {
            User user = new User();
            user.setUsername("admin");
            user.setPassword(passwordEncoder.encode("admin"));
            user.setAccountExpireTime(LocalDateTime.now().plusYears(100));
            repository.save(user);
        } catch (Exception e) {

        }

    }

    /**
     * Load user by username user details.
     *
     * @param username the username
     * @return the user details
     */
    @Override
    public UserDetails loadUserByUsername(String username) {
        User user = repository.findByUsername(username);
        if (Objects.isNull(user)) {
            throw new UsernameNotFoundException("用户名不存在");
        }
        return user;
    }

    /**
     * Insert oauth 2 user model.
     *
     * @param user the user
     * @return the oauth 2 user model
     */
    @Override
    public Oauth2UserModel insert(User user) {
        if (repository.countByUsername(user.getUsername()) > 0) {
            throw new GlobalException(GlobalResultCode.USERNAME_ALREADY_EXIST);
        }
        check(user);
        user = repository.save(user);
        Oauth2UserModel oauth2UserModel = new Oauth2UserModel();
        BeanUtils.copyProperties(user, oauth2UserModel);
        return oauth2UserModel;
    }

    /**
     * Check.
     *
     * @param user the user
     */
    private void check(User user) {
        long countByEmail;
        long countByMobile;
        String email = user.getEmail();
        String mobile = user.getMobile();
        if (Objects.nonNull(user.getId())) {
            Long id = user.getId();
            countByEmail = repository.countByEmailAndIdIsNot(email, id);
            countByMobile = repository.countByMobileAndIdIsNot(mobile, id);
        } else {
            countByEmail = repository.countByEmail(email);
            countByMobile = repository.countByMobile(mobile);
        }
        if (countByEmail > 0) {
            throw new GlobalException(GlobalResultCode.EMAIL_ALREADY_EXIST);
        }

        if (countByMobile > 0) {
            throw new GlobalException(GlobalResultCode.MOBILE_ALREADY_EXIST);
        }
    }

    /**
     * Update oauth 2 user model.
     *
     * @param user the user
     * @return the oauth 2 user model
     */
    @Override
    public Oauth2UserModel update(User user) {
        check(user);
        user = repository.save(user);
        Oauth2UserModel oauth2UserModel = new Oauth2UserModel();
        BeanUtils.copyProperties(user, oauth2UserModel);
        return oauth2UserModel;
    }

    /**
     * Update password boolean.
     *
     * @param password the password
     * @param id       the id
     * @return the boolean
     */
    @Override
    public boolean updatePassword(String password, Long id) {
        return repository.updatePassword(password, id) > 0;
    }

    /**
     * Delete user boolean.
     *
     * @param id the id
     * @return the boolean
     */
    @Override
    public boolean deleteUser(Long id) {
        return repository.deleteUserById(id) > 0;
    }

    /**
     * Find user oauth 2 user model.
     *
     * @param model the model
     * @return the oauth 2 user model
     */
    @Override
    public Oauth2UserModel findUser(Oauth2UserQueryModel model) {
        UserQueryModel queryModel = new UserQueryModel();
        BeanUtils.copyProperties(model, queryModel);
        Specification<User> specification = jpaHandler.buildSpecification(queryModel);
        Optional<User> optional = repository.findOne(specification);
        if (!optional.isPresent()) {
            return null;
        }
        User user = optional.get();
        Oauth2UserModel oauth2UserModel = new Oauth2UserModel();
        BeanUtils.copyProperties(user, oauth2UserModel);
        return oauth2UserModel;
    }

    /**
     * Check exist boolean.
     *
     * @param model the model
     * @return the boolean
     */
    @Override
    public boolean checkExist(Oauth2UserQueryModel model) {
        UserQueryModel queryModel = new UserQueryModel();
        BeanUtils.copyProperties(model, queryModel);
        Specification<User> specification = jpaHandler.buildSpecification(queryModel);
        long count = repository.count(specification);
        return count > 0;
    }
}
